/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */

/*
 * ShapeViewer.java
 *
 * Created on 25.07.2009, 14:35:23
 */

package dipl.visualizer.ui;

import dipl.algorithm.math.fp.primitive.Matrix3x3f;
import dipl.algorithm.math.fp.primitive.Point2df;
import dipl.algorithm.math.fp.primitive.Point3df;
import dipl.algorithm.math.fp.primitive.op.AffineTransformations2d;
import dipl.visualizer.graphics.Matrix3x3Ops;
import dipl.visualizer.graphics.PaintContext;
import dipl.visualizer.graphics.shape.IShape;
import java.awt.Color;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Point;
import java.awt.RenderingHints;
import java.util.LinkedList;

/**
 *
 * @author Dietmar Walther
 */
public class ShapeViewer extends javax.swing.JPanel {

	/** Creates new form ShapeViewer */
	public ShapeViewer() {
		initComponents();
		drawBorder = true;
		shapes = new LinkedList<IShape>();
		displayNames = false;
		displayCoordSystem = true;
		lastMousePoint = new Point( 0, 0 );
		ptXLeft = new Point3df();
		ptXRight = new Point3df();
		ptYTop = new Point3df();
		ptYBottom = new Point3df();
		SetCoordinateSystem( -1.0, 1.0, -1.0, 1.0 );
	}

	//
	// PUBLIC METHODS
	//


	public void AddShape( IShape shape ) {
		shapes.addLast( shape );
	}

	public void Clear() {
		shapes.clear();
	}

	public boolean DisplayNames() {
		return displayNames; 
	}

	public void SetDisplayNames( boolean set ) {
		displayNames = set;
	}

	public void Flush() {
		this.repaint();
	}

	public void RemoveShape( IShape shape ) {
		shapes.remove( shape );
	}

	/// <summary>
	/// Sets world coordinate system used to display shapes.
	/// </summary>
	/// <param name="xl">minimum x-ccordinate</param>
	/// <param name="xu">maximum x-ccordinate</param>
	/// <param name="yl">minimum y-ccordinate</param>
	/// <param name="yu">maximum y-ccordinate</param>
	public void SetCoordinateSystem( double xl, double xu, double yl, double yu ) {
		csXL = xl;
		csYB = yl;
		csXR = xu;
		csYT = yu;
		CreateCoordTransform();
	}

	/// <summary>
	/// Zooms shape view by given amount (0.0 means no zoom, &lt; 0.0 means zoom-out, &gt; 0.0 means zoom-in)
	/// </summary>
	/// <param name="zoomx"></param>
	/// <param name="zoomy"></param>
	public void ZoomBy( double zoomx, double zoomy ) {
		double dx = zoomx*(csXR-csXL);
		double dy = zoomy*(csYT-csYB);
		SetCoordinateSystem( csXL+dx, csXR-dx, csYB+dy, csYT-dy );
		repaint();
	}

	//
	// PAINTING METHODS
	//

	@Override
	public void paintComponent(Graphics g) {
		super.paintComponent(g);
		Graphics2D g2 = (Graphics2D)g;
		
		g2.setBackground( Color.white );
		g2.clearRect( 0, 0, getWidth()-1, getHeight()-1 );
		if( drawBorder ) {
			g2.setColor( Color.blue );
			g2.drawRect( 0, 0, getWidth()-1, getHeight()-1 );
		}

		if( displayCoordSystem ) {
			g2.setColor( Color.lightGray );
			g2.drawLine( (int)ptXLeft.x, (int)ptXLeft.y, (int)ptXRight.x, (int)ptXRight.y );
			g2.drawLine( (int)ptYTop.x, (int)ptYTop.y, (int)ptYBottom.x, (int)ptYBottom.y );
		}

		if( shapes.size() == 0 )
			return;

		g2.setRenderingHint( RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_SPEED );
		g2.setRenderingHint( RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON );
		g2.setRenderingHint( RenderingHints.KEY_COLOR_RENDERING, RenderingHints.VALUE_COLOR_RENDER_SPEED );
		g2.setRenderingHint( RenderingHints.KEY_ALPHA_INTERPOLATION, RenderingHints.VALUE_ALPHA_INTERPOLATION_SPEED );

		PaintContext pc = new PaintContext( g2 );
		pc.SetTransform( coordTransform );

		for( IShape sh : shapes ) {
			sh.Paint( pc );
			if( displayNames ) {
				Point2df ptShape = sh.PointOnShape();
				pc.SetFont( this.getFont() );
				pc.SetColor( Color.black );
				pc.DrawString( sh.Name(), ptShape.x, ptShape.y );
			}
		}
	}

	//
  // AUXILLIARY METHODS
	//
	
	/// <summary>
	/// recreates world transformation matrix
	/// </summary>
	protected void CreateCoordTransform() {

		ptXLeft.Set( csXL, 0.0, 1.0 );
		ptXRight.Set( csXR, 0.0, 1.0 );
		ptYTop.Set( 0.0, csYT, 1.0 );
		ptYBottom.Set( 0.0, csYB, 1.0 );

		// world to coordinate system window transformation
		// | ux uy 0 |
		// | vx vy 0 |
		// | ox oy 1 |
		double ox = (csXL+csXR)*0.5; double oy = (csYB+csYT)*0.5;
		double ux = csXR-ox; double uy = 0.0;
		double vx = 0.0; double vy = csYT-oy;
		Matrix3x3f w2wTransform = new Matrix3x3f();
		w2wTransform.m[0][0] = ux; w2wTransform.m[0][1] = uy; w2wTransform.m[0][2] = 0.0;
		w2wTransform.m[1][0] = vx; w2wTransform.m[1][1] = vy; w2wTransform.m[1][2] = 0.0;
		w2wTransform.m[2][0] = ox; w2wTransform.m[2][1] = oy; w2wTransform.m[2][2] = 1.0;
		try {
			Matrix3x3Ops.invert( w2wTransform );
		}
		catch( Exception e ) {
			e.printStackTrace();
			return;
		}
		// viewport transform
		double clientSX = getWidth();
		double clientSY = getHeight();

		Matrix3x3f vpTransform = new Matrix3x3f();
		vpTransform.m[0][0] = clientSX*0.5;
		vpTransform.m[1][1] = -clientSY*0.5;
		vpTransform.m[2][0] = clientSX*0.5; vpTransform.m[2][1] = clientSY*0.5;
		coordTransform = Matrix3x3Ops.multiply( w2wTransform, vpTransform );

		// transform coordinate axes
		AffineTransformations2d.TransformPoint( coordTransform, ptXLeft );
		AffineTransformations2d.TransformPoint( coordTransform, ptXRight );
		AffineTransformations2d.TransformPoint( coordTransform, ptYTop );
		AffineTransformations2d.TransformPoint( coordTransform, ptYBottom );

		ptXLeft.ToCartesian();
		ptXRight.ToCartesian();
		ptYTop.ToCartesian();
		ptYBottom.ToCartesian();
	}

	/** This method is called from within the constructor to
	 * initialize the form.
	 * WARNING: Do NOT modify this code. The content of this method is
	 * always regenerated by the Form Editor.
	 */
	@SuppressWarnings("unchecked")
  // <editor-fold defaultstate="collapsed" desc="Generated Code">//GEN-BEGIN:initComponents
  private void initComponents() {

    setName("Form"); // NOI18N
    addMouseWheelListener(new java.awt.event.MouseWheelListener() {
      public void mouseWheelMoved(java.awt.event.MouseWheelEvent evt) {
        OnMouseWheel(evt);
      }
    });
    addMouseListener(new java.awt.event.MouseAdapter() {
      public void mousePressed(java.awt.event.MouseEvent evt) {
        OnMousePressed(evt);
      }
    });
    addComponentListener(new java.awt.event.ComponentAdapter() {
      public void componentResized(java.awt.event.ComponentEvent evt) {
        OnResize(evt);
      }
    });
    addMouseMotionListener(new java.awt.event.MouseMotionAdapter() {
      public void mouseDragged(java.awt.event.MouseEvent evt) {
        OnMouseDragged(evt);
      }
    });
    addFocusListener(new java.awt.event.FocusAdapter() {
      public void focusGained(java.awt.event.FocusEvent evt) {
        OnFocusGained(evt);
      }
      public void focusLost(java.awt.event.FocusEvent evt) {
        OnFocusLost(evt);
      }
    });

    javax.swing.GroupLayout layout = new javax.swing.GroupLayout(this);
    this.setLayout(layout);
    layout.setHorizontalGroup(
      layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
      .addGap(0, 400, Short.MAX_VALUE)
    );
    layout.setVerticalGroup(
      layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
      .addGap(0, 300, Short.MAX_VALUE)
    );
  }// </editor-fold>//GEN-END:initComponents

	private void OnResize(java.awt.event.ComponentEvent evt) {//GEN-FIRST:event_OnResize
		CreateCoordTransform();
    repaint();
	}//GEN-LAST:event_OnResize

	private void OnFocusGained(java.awt.event.FocusEvent evt) {//GEN-FIRST:event_OnFocusGained
      this.drawBorder = true;
      CreateCoordTransform();
			repaint();
	}//GEN-LAST:event_OnFocusGained

	private void OnFocusLost(java.awt.event.FocusEvent evt) {//GEN-FIRST:event_OnFocusLost
      this.drawBorder = false;
      CreateCoordTransform();
			repaint();
	}//GEN-LAST:event_OnFocusLost

	private void OnMousePressed(java.awt.event.MouseEvent evt) {//GEN-FIRST:event_OnMousePressed
		if( evt.getButton() == java.awt.event.MouseEvent.BUTTON1 ) {
			lastMousePoint = evt.getPoint();
			this.requestFocus();
		}
	}//GEN-LAST:event_OnMousePressed

	private void OnMouseWheel(java.awt.event.MouseWheelEvent evt) {//GEN-FIRST:event_OnMouseWheel
		double sgnDelta = (double)Math.signum( evt.getWheelRotation() );
		ZoomBy( -sgnDelta*0.05, -sgnDelta*0.05 );
	}//GEN-LAST:event_OnMouseWheel

	private void OnMouseDragged(java.awt.event.MouseEvent evt) {//GEN-FIRST:event_OnMouseDragged
		Point mp = evt.getPoint();
		double dx = -(double)(mp.x-lastMousePoint.x)/(double)getWidth()*(csXR-csXL);
		double dy = (double)(mp.y-lastMousePoint.y)/(double)getHeight()*(csYT-csYB);
		SetCoordinateSystem( csXL+dx, csXR+dx, csYB+dy, csYT+dy );
		lastMousePoint = mp;
		repaint();
	}//GEN-LAST:event_OnMouseDragged


  // Variables declaration - do not modify//GEN-BEGIN:variables
  // End of variables declaration//GEN-END:variables

	//
	// MEMBERS
	//
	
	/// <summary>
	/// viewed polygons
	/// </summary>
	private LinkedList<IShape> shapes;

	/// <summary>
	/// world transformation
	/// </summary>
	private Matrix3x3f coordTransform;

	/// <summary>
	/// world coordinate system right x-border
	/// </summary>
	private double csXR;
	/// <summary>
	/// world coordinate system left x-border
	/// </summary>
	private double csXL;
	/// <summary>
	/// world coordinate system top y-border
	/// </summary>
	private double csYT;
	/// <summary>
	/// world coordinate system bottom y-border
	/// </summary>
	private double csYB;

	/// <summary>
	/// Whether or not to display names of shapes
	/// </summary>
	private boolean displayNames;

	/// <summary>
	/// display coordinate system?
	/// </summary>
	private boolean displayCoordSystem;

	/// <summary>
	/// last mouse point
	/// </summary>
	private Point lastMousePoint;

	/// <summary>
	/// transformed top point of y-axis
	/// </summary>
	private Point3df ptYTop;

	/// <summary>
	/// transformed bottom point of y-axis
	/// </summary>
	private Point3df ptYBottom;

	/// <summary>
	/// transformed left point of x-axis
	/// </summary>
	private Point3df ptXLeft;

	/// <summary>
	/// transformed right point of x-axis
	/// </summary>
	private Point3df ptXRight;

	/// <summary>
	/// whether or not border should be drawn
	/// </summary>
	private boolean drawBorder;

}
